/*
 * Copyright (c) 2024 Shenzhen Kaihong Digital Industry Development Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "sle_config_manager.h"
#include "sle_hilog.h"
#include <regex>
#include <sys/stat.h>
#include <iostream>

namespace OHOS {
namespace NearLink {
namespace SleStandard {

SleConfigManager::SleConfigManager(std::string fileName)
{
    SleErrorCode ret = SLE_ERR_UNKNOWN;
    SLE_ASSERT_AND_RETURN(fileName.empty() == false);
    fileName_ = fileName;
    //检查SLE_FILE_PATH下是否有文件,若无则从SLE_FILE_DEFAULT_PATH处copy来
    std::string fileNameStr = SLE_FILE_PATH + fileName_;
    std::ifstream configFile(fileNameStr.c_str());
    if (configFile.is_open() == false) {
        SLE_LOGI("%s can't open, try to copy it form default file", fileNameStr.c_str());
        ret = ResetDefaultConfigFile();
        SLE_ASSERT_AND_RETURN_LOG(ret == SLE_NO_ERROR, "ResetDefaultConfigFile fail(%d)", ret);
    }
    ret = UpdateValueFromFile();
    SLE_ASSERT_AND_RETURN_LOG(ret == SLE_NO_ERROR, "UpdateValueFromFile fail(%d)", ret);
}

SleConfigManager::~SleConfigManager()
{
    SleErrorCode ret = UpdateValueToFile();
    SLE_ASSERT_AND_RETURN(ret == SLE_NO_ERROR);
}

void SleConfigManager::TrimString(std::string &str)
{
    int i = 0;
    int j = static_cast<int>(str.length()) - 1;
    while (i < static_cast<int>(str.length()) && ((str[i] == ' ') || (str[i] == '\t') || (str[i] == '\n'))) {
        ++i;
    }
    while (j >= 0 && (str[j] == ' ' || (str[j] == '\t') || (str[j] == '\n'))) {
        --j;
    }
    str = ((i > j) ? "" : str.substr(i, j - i + 1));
}

SleErrorCode SleConfigManager::UpdateValueFromFile(void)
{
    SLE_ASSERT_AND_RETURN_RET(fileName_.empty() == false, SLE_ERR_NOT_READY);
    std::string fileName = SLE_FILE_PATH + fileName_;
    std::ifstream configFile(fileName.c_str());
    SLE_ASSERT_AND_RETURN_RET(configFile.is_open() == true, SLE_ERR_SYSTEM_WRONG);
    valueMap_.clear();
    std::string line;
    std::string tag;
    std::string key;
    std::string value;
    std::regex tagRegex(R"(\[(.*?)\])");        //正则表达式用于匹配[xxx]标签行
    std::smatch match;
    SleConfigValue configValue;
    while (std::getline(configFile, line)) {
        TrimString(line);
        if (line.empty() || line.substr(0, 1) == "#") {
            continue;
        }
        if (std::regex_search(line, match, tagRegex) && match.size() > 1) {
            tag= match[1].str();
            continue;
        }
        if(line.back() == '}') {
            valueMap_[tag] = configValue;
            configValue.clear();
            continue;
        }
        if(line.find('=') != std::string::npos) {
            std::stringstream ss(line);
            std::getline(ss, key, '=');
            std::getline(ss, value);
            TrimString(key);
            TrimString(value);
            configValue[key] = value;
        }
    }
    PrintSleConfigValueMap(valueMap_);
    return SLE_NO_ERROR;
}

SleErrorCode SleConfigManager::UpdateValueToFile(void)
{
    std::string fileName = SLE_FILE_PATH + fileName_;
    std::ofstream configFile(fileName.c_str(), std::ios::out | std::ios::trunc);
    SLE_ASSERT_AND_RETURN_RET(configFile.is_open() == true, SLE_ERR_SYSTEM_WRONG);
    for(auto iterMap : valueMap_) {
        configFile << "[" << iterMap.first.c_str() << "]" << " " << "{" << std::endl;
        for(auto itor : iterMap.second) {
            configFile << "\t" << itor.first.c_str() << "=" << itor.second.c_str() << std::endl;
        }
        configFile << "}" << std::endl;
    }
    return SLE_NO_ERROR;
}

SleErrorCode SleConfigManager::ResetDefaultConfigFile(void)
{
    std::string fileName = SLE_FILE_DEFAULT_PATH + fileName_;
    std::ifstream defaultFile(fileName.c_str());
    SLE_ASSERT_AND_RETURN_RET(defaultFile.is_open() == true, SLE_ERR_SYSTEM_WRONG);
    fileName = SLE_FILE_PATH + fileName_;
    std::ofstream configFile(fileName.c_str(), std::ios::out | std::ios::trunc);
    SLE_ASSERT_AND_RETURN_RET_LOG(configFile.is_open() == true, SLE_ERR_SYSTEM_WRONG
        , "create %s fail(%d,%s)", fileName.c_str(), errno, std::strerror(errno));
    std::copy(std::istreambuf_iterator<char>(defaultFile),
              std::istreambuf_iterator<char>(),
              std::ostreambuf_iterator<char>(configFile));
    return SLE_NO_ERROR;
}

SleErrorCode SleConfigManager::GetValue(SleConfigValueMap &values)
{
    values = valueMap_;
    return SLE_NO_ERROR;
}

SleErrorCode SleConfigManager::SetValue(const SleConfigValueMap &values)
{
    valueMap_ = values;
    SleErrorCode ret =  UpdateValueToFile();
    SLE_ASSERT_AND_RETURN_RET(ret == SLE_NO_ERROR, ret);
    return SLE_NO_ERROR;
}

uint8_t SleConfigManager::Str2Uint8(const std::string &str)
{
    SLE_ASSERT_AND_RETURN_RET(str.empty() == false, 0);
    int value = std::stoi(str);
    if (value < 0 || value > UINT8_MAX) {
        SLE_LOGE("value(%d) out of uint8_t range", value);
    }
    uint8_t ret= (uint8_t)value;
    return ret;
}

uint16_t SleConfigManager::Str2Uint16(const std::string &str)
{
    SLE_ASSERT_AND_RETURN_RET(str.empty() == false, 0);
    int value = std::stoi(str);
    if (value < 0 || value > 65535) {
        SLE_LOGE("value(%d) out of uint16_t range", value);
    }
    return static_cast<uint16_t>(value);
}

uint32_t SleConfigManager::Str2Uint32(const std::string &str)
{
    SLE_ASSERT_AND_RETURN_RET(str.empty() == false, 0);
    unsigned long value = std::stoul(str);
    if (value > UINT32_MAX) {
        SLE_LOGE("value(%lu) out of uint32_t range", value);
    }
    return static_cast<uint32_t>(value);
}

bool SleConfigManager::str2Bool(const std::string &str) {
    SLE_ASSERT_AND_RETURN_RET(str.empty() == false, false);
    std::string lowerStr = str;
    std::transform(lowerStr.begin(), lowerStr.end(), lowerStr.begin(),
                   [](unsigned char c) { return std::tolower(c); });
    bool ret = lowerStr == "true" || lowerStr == "1" || lowerStr == "yes" || lowerStr == "on";
    return ret;
}

void SleConfigManager::PrintSleConfigValueMap(const SleConfigValueMap values)
{
    //print config
    for (const auto& outerPair : values) {
        const std::string& outerKey = outerPair.first;
        const SleConfigValue& innerMap = outerPair.second;
        SLE_LOGD("Section = %{public}s", outerKey.c_str());
        for (const auto& innerPair : innerMap) {
            const std::string& innerKey = innerPair.first;
            const std::string& innerValue = innerPair.second;
            SLE_LOGD("%{public}s = %{public}s", innerKey.c_str(), innerValue.c_str());
        }
    }
}

} // SleStandard
} // NearLink
} // OHOS